package Snake
{
	import GameEvent.CGameEvent_EndShowTime;
	import GameEvent.CGameEvent_SnakeEatSomthing;
	
	import Item.CItem;
	import Item.CItem_Virus;
	
	import Map.CMap;
	import Map.CMapTile;
	import Map.CMapTile_Door;
	
	import flash.display.Sprite;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	
	public class CSnake
	{
		private const STATE_NORMAL			:uint = 0;
		private const STATE_SHOWTIME		:uint = 1;
		private const STATE_STURN			:uint = 2;
		private var m_state :uint;
		
		private var m_bIsStun :Boolean;
		
		private const HEAD_INDEX			:uint = 0;
		private const DEFAULT_MOVE_DELAY	:Number = 0.4;
		private const MIN_MOVE_DELAY		:Number = 0.10;
		
		private const DEFAULT_MOVE_SPEED	:Number = 50;
		private const ACC_MOVE_SPEED		:Number = 2;
		private const MAX_MOVE_SPEED		:Number = 150;
		
		private const TIME_SHOWTIME			:Number = 10;
		private const TIME_STURN			:Number = 0.2;
		
		private var m_sprite :Sprite;
		
		private var m_refMap :CMap;
		private var m_arrBodyPiece :Array;
		private var m_nMoveDelay :Number;
		private var m_nElapsedTimeToMove :Number;
		private var m_nElapsedTimeToShowTime :Number;
		private var m_nElapsedTimeToStrun :Number;
		private var m_nMoveDirection :uint;
		private var m_nTurnDirection :uint;
		private var m_bIsDie :Boolean;
		private var m_nKeyElapsedTime :Number = 0;
		
		private var m_nMoveSpeed :Number;
		
		public function get sprite() :Sprite { return m_sprite; }
		public function get nBodyPieceCount() :uint { return m_arrBodyPiece.length; }
		public function get nMoveSpeed() :Number { return m_nMoveSpeed; }
		
		public function CSnake(refMap :CMap)
		{
			m_sprite = new Sprite;
			m_refMap = refMap;
			m_arrBodyPiece = new Array();
			
			Clear();
		}
		public function Clear() :void
		{
			m_bIsStun = false;
			m_state = STATE_NORMAL;
			var refSnakeBodyPiece :CSnakePiece = null;
			for (var i :uint = 0; i < m_arrBodyPiece.length; ++i)
			{
				refSnakeBodyPiece = m_arrBodyPiece[i];
				sprite.removeChild(refSnakeBodyPiece.sprite);
			}
			m_arrBodyPiece.splice(0, m_arrBodyPiece.length);
			
			m_nMoveDelay = DEFAULT_MOVE_DELAY;
			m_nElapsedTimeToMove = 0;
			m_nElapsedTimeToShowTime = 0;
			m_nElapsedTimeToStrun = 0;
			m_nMoveDirection = CSnakePiece.MOVE_DIRECTION_DOWN;
			m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_NONE;
			m_nMoveSpeed = DEFAULT_MOVE_SPEED;
		}
		public function Init() :void
		{
			CreateHead(1, 4);
			CreateBodyPiece(1, 3);
			CreateBodyPiece(2, 3);
			CreateBodyPiece(2, 2);
			CreateBodyPiece(1, 2);
			CreateBodyPiece(1, 1);
			CreateBodyPiece(2, 1);
			
			for (var i :uint = 0; i < CGameScoreManager.instance.GetAddPieceCount(); ++i)
			{
				AddBodyPiece();
			}
			CGameScoreManager.instance.ResetAddPieceCount();
			
			var cSnakeBodyPiece :CSnakePiece = m_arrBodyPiece[HEAD_INDEX];
			cSnakeBodyPiece.SetMoveInfo(m_nMoveDirection);
		}
		public function GetBodyPiece(i :uint) :CSnakePiece
		{
			if (i >= m_arrBodyPiece.length) return null;
			return m_arrBodyPiece[i];
		}
		public function Update(tick :Number) :void
		{
			if (m_bIsStun == true)
			{
				OnState_Sturn(tick);
				return;
			}
			else
			{
				switch (m_state)
				{
				case STATE_NORMAL:		OnState_Normal(tick);	break;
				case STATE_SHOWTIME:	OnState_ShowTime(tick);	break;
				}
			}
		}
		private function OnState_Normal(tick :Number) :void
		{
			ProcessKeyInput();
			
			var refSnakeHeadPiece :CSnakePiece = m_arrBodyPiece[HEAD_INDEX];
			if (refSnakeHeadPiece != null)
			{
				refSnakeHeadPiece.Update(tick);
				
				if (refSnakeHeadPiece.IsIdle() == true)
				{
					ProcessTurn();
					ProcessMove();
				}
			}
		}
		private function OnState_ShowTime(tick :Number) :void
		{
			m_nMoveDirection = CSnakePiece.MOVE_DIRECTION_NONE;
			
			m_nKeyElapsedTime += tick;
			if (m_nKeyElapsedTime > 0.1)
			{
				ProcessKeyInput();
				if (m_nTurnDirection != CSnakePiece.MOVE_DIRECTION_NONE)
					m_nKeyElapsedTime = 0;
			}
			
			var refSnakeHeadPiece :CSnakePiece = m_arrBodyPiece[HEAD_INDEX];
			if (refSnakeHeadPiece != null)
			{
				refSnakeHeadPiece.Update(tick);
				
				if (refSnakeHeadPiece.IsIdle() == true)
				{
					ProcessTurn();
					ProcessMove();
				}
			}
			
			m_nElapsedTimeToShowTime += tick;
			if (m_nElapsedTimeToShowTime > TIME_SHOWTIME)
			{
				m_nElapsedTimeToShowTime = 0;
				m_state = STATE_NORMAL;
				CalculateMoveDelay();
				
				var eventEndShowTime :CGameEvent_EndShowTime = null;
				eventEndShowTime = new CGameEvent_EndShowTime();
				sprite.dispatchEvent(eventEndShowTime);
			}
		}
		private function OnState_Sturn(tick :Number) :void
		{
			ProcessKeyInput();
			
			m_nElapsedTimeToStrun += tick;
			if (m_nElapsedTimeToStrun > TIME_STURN)
			{
				m_nElapsedTimeToStrun = 0;
				m_bIsStun = false;
				ProcessTurn();
			}
		}
		
		public function ProcessCollision_Item(refItem :CItem) :Boolean
		{
			var eventEatSomthing :CGameEvent_SnakeEatSomthing = null;
			if (refItem.nType == CItem.TYPE_FOOD)
			{
				AddBodyPiece();
				
				eventEatSomthing = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_ITEM_FOOD);
			}
			else if (refItem.nType == CItem.TYPE_VIRUS)
			{
				var refVirus :CItem_Virus = CItem_Virus(refItem); 
				if (refVirus.state == CItem_Virus.STATE_READY) return false;
				
				var nDeleteIndex :uint = 0;
				if (m_arrBodyPiece.length > 2) nDeleteIndex = m_arrBodyPiece.length-2;
				DeleteBodyPiece(nDeleteIndex);
				
				eventEatSomthing = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_ITEM_VIRUS);
			}
			else if (refItem.nType == CItem.TYPE_COIN)
			{
				eventEatSomthing = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_ITEM_COIN);
			}
			else if (refItem.nType == CItem.TYPE_GOLD)
			{
				eventEatSomthing = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_ITEM_GOLD);
			}
			else if (refItem.nType == CItem.TYPE_SHOWTIME)
			{
				m_nElapsedTimeToShowTime = 0;
				m_state = STATE_SHOWTIME;
				CalculateMoveDelay();
				eventEatSomthing = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_ITEM_SHOWTIME);
			}
			
			if (eventEatSomthing != null)
				sprite.dispatchEvent(eventEatSomthing);
				
			return true;
		}
		public function ProcessCollision_BodyPiece(index :uint) :void
		{
			DeleteBodyPiece(index);
			
			var eventEatBody :CGameEvent_SnakeEatSomthing;
			eventEatBody = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_BODY);
			
			sprite.dispatchEvent(eventEatBody);
		}
		public function CheatAction_AddBodyPiece() :void /// 치트 액션
		{
			AddBodyPiece();
		}
		
		private function ProcessKeyInput() :void
		{
			if (CGame.KeyData.GetKeyState(Keyboard.LEFT) == true)
			{
				m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_LEFT;
			}
			else if (CGame.KeyData.GetKeyState(Keyboard.RIGHT) == true)
			{
				m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_RIGHT;
			}
			else if (CGame.KeyData.GetKeyState(Keyboard.UP) == true)
			{
				m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_UP;
			}
			else if (CGame.KeyData.GetKeyState(Keyboard.DOWN) == true)
			{
				m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_DOWN;
			}
		}
		private function ProcessTurn() :void
		{
			var refHeadPiece :CSnakePiece = m_arrBodyPiece[HEAD_INDEX];
			if (refHeadPiece == null) return;

			var refNeckPiece :CSnakePiece = m_arrBodyPiece[1];
			if (refNeckPiece == null) return;
			
			if (m_nTurnDirection == CSnakePiece.MOVE_DIRECTION_LEFT &&
				m_nMoveDirection != CSnakePiece.MOVE_DIRECTION_RIGHT &&
				refNeckPiece.GetMapPosX() != refHeadPiece.GetMapPosX()-1 )
			{
				m_nMoveDirection = CSnakePiece.MOVE_DIRECTION_LEFT;
			}
			else if (m_nTurnDirection == CSnakePiece.MOVE_DIRECTION_RIGHT &&
					m_nMoveDirection != CSnakePiece.MOVE_DIRECTION_LEFT &&
					refNeckPiece.GetMapPosX() != refHeadPiece.GetMapPosX()+1)
			{
				m_nMoveDirection = CSnakePiece.MOVE_DIRECTION_RIGHT;
			}
			else if (m_nTurnDirection == CSnakePiece.MOVE_DIRECTION_UP &&
					m_nMoveDirection != CSnakePiece.MOVE_DIRECTION_DOWN &&
					refNeckPiece.GetMapPosY() != refHeadPiece.GetMapPosY()-1)
			{
				m_nMoveDirection = CSnakePiece.MOVE_DIRECTION_UP;
			}
			else if (m_nTurnDirection == CSnakePiece.MOVE_DIRECTION_DOWN &&
					m_nMoveDirection != CSnakePiece.MOVE_DIRECTION_UP &&
					refNeckPiece.GetMapPosY() != refHeadPiece.GetMapPosY()+1)
			{
				m_nMoveDirection = CSnakePiece.MOVE_DIRECTION_DOWN;
			}
			m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_NONE;
		}
		private function ProcessMove() :void
		{
			if (CSnakePiece.MOVE_DIRECTION_NONE == m_nMoveDirection) return;
			
			var refSnakeHeadPiece :CSnakePiece = m_arrBodyPiece[HEAD_INDEX];
			if (refSnakeHeadPiece == null) return;
			
			var nDestMapPosX :int = refSnakeHeadPiece.GetMapPosX();
			var nDestMapPosY :int = refSnakeHeadPiece.GetMapPosY();
			switch(m_nMoveDirection)
			{
			case CSnakePiece.MOVE_DIRECTION_UP:		nDestMapPosY--;	break;
			case CSnakePiece.MOVE_DIRECTION_DOWN:	nDestMapPosY++;	break;
			case CSnakePiece.MOVE_DIRECTION_LEFT:	nDestMapPosX--;	break;
			case CSnakePiece.MOVE_DIRECTION_RIGHT:	nDestMapPosX++;	break;
			}
			
			var refMapTile :CMapTile = m_refMap.GetMapTile(nDestMapPosX, nDestMapPosY);
			if (refMapTile == null) return;
			
			if (refMapTile.nType == CMapTile.TYPE_BLOCK)
			{
				DeleteBodyPiece(m_arrBodyPiece.length-1);
				
				var eventEatBlock :CGameEvent_SnakeEatSomthing;
				eventEatBlock = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_BLOCK);
				
				sprite.dispatchEvent(eventEatBlock);
				
				//m_state = STATE_STURN;
				m_bIsStun = true;
			}
			else if (refMapTile.nType == CMapTile.TYPE_DOOR)
			{
				var refMapTile_Door :CMapTile_Door = refMapTile as CMapTile_Door;
				if (refMapTile_Door.nDoorHP > 0)
				{
					DeleteBodyPiece(m_arrBodyPiece.length-1);
					refMapTile_Door.DecreaseDoorHP();
				
					var eventEatDoor :CGameEvent_SnakeEatSomthing;
					eventEatDoor = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_DOOR);
					
					sprite.dispatchEvent(eventEatDoor);
					
					//m_state = STATE_STURN;
					m_bIsStun = true;
				}
				else
				{
					refSnakeHeadPiece.SetMoveInfo(m_nMoveDirection);
				}
			}
			else if (refMapTile.nType == CMapTile.TYPE_GOAL)
			{
				var eventEatGoal :CGameEvent_SnakeEatSomthing;
				eventEatGoal = new CGameEvent_SnakeEatSomthing(CGameEvent_SnakeEatSomthing.TYPE_GOAL);
				
				sprite.dispatchEvent(eventEatGoal);
			}
			else
			{
				refSnakeHeadPiece.SetMoveInfo(m_nMoveDirection);
			}
		}
		private function CreateHead(nMapPosX :int, nMapPosY :int) :void
		{
			var cSnakeBodyPiece :CSnakePiece = new CSnakePiece(this, CSnakePiece.PIECE_TYPE_HEAD, nMapPosX, nMapPosY);
			sprite.addChild(cSnakeBodyPiece.sprite);
			m_arrBodyPiece.push(cSnakeBodyPiece);
		}
		private function CreateBodyPiece(nMapPosX :int, nMapPosY :int) :void
		{
			var cForwardPiece :CSnakePiece = m_arrBodyPiece[m_arrBodyPiece.length-1];
			
			var cSnakeBodyPiece :CSnakePiece = null;
			cSnakeBodyPiece = new CSnakePiece(this, CSnakePiece.PIECE_TYPE_BODY, nMapPosX, nMapPosY);
			sprite.addChildAt(cSnakeBodyPiece.sprite, 0);
			m_arrBodyPiece.push(cSnakeBodyPiece);
			
			cForwardPiece.SetBackPiece(cSnakeBodyPiece);
			
			CalculateMoveDelay();
		}
		private function AddBodyPiece() :void
		{
			var cForwardPiece :CSnakePiece = m_arrBodyPiece[m_arrBodyPiece.length-1];
			
			var cSnakeBodyPiece :CSnakePiece = null;
			cSnakeBodyPiece = new CSnakePiece(this, CSnakePiece.PIECE_TYPE_BODY, cForwardPiece.GetMapPosX(), cForwardPiece.GetMapPosY());
			sprite.addChildAt(cSnakeBodyPiece.sprite, 0);
			m_arrBodyPiece.push(cSnakeBodyPiece);
			
			cForwardPiece.SetBackPiece(cSnakeBodyPiece);
			
			CalculateMoveDelay();
		}
		private function DeleteBodyPiece(index :uint) :void
		{
			if (index >= m_arrBodyPiece.length)
			{
				trace("Error! CSnake.DeleteBodyPiece() " + index);
				return;
			}
			
			var nDeleteStartIndex :uint = index;
			var nDeletedCount :uint = 0;
			for ( ; index < m_arrBodyPiece.length; ++index)
			{
				var cSnakeBodyPiece :CSnakePiece = m_arrBodyPiece[index];
				sprite.removeChild(cSnakeBodyPiece.sprite);
				cSnakeBodyPiece.Destroy();
				m_arrBodyPiece[index] = null;
				nDeletedCount++;
			}
			m_arrBodyPiece.splice(nDeleteStartIndex, nDeletedCount);
			
			CalculateMoveDelay();
		}
		private function CalculateMoveDelay() :void
		{
			if (m_state == STATE_SHOWTIME)
			{
				m_nMoveSpeed = MAX_MOVE_SPEED;
			}
			else
			{
				m_nMoveSpeed = DEFAULT_MOVE_SPEED + (ACC_MOVE_SPEED*nBodyPieceCount);
				if (m_nMoveSpeed > MAX_MOVE_SPEED)
					m_nMoveSpeed = MAX_MOVE_SPEED;
			}
		}
		private function OnKeyDownForShowTime(event :KeyboardEvent) :void
		{
			var refHeadPiece :CSnakePiece = m_arrBodyPiece[HEAD_INDEX];
			if (refHeadPiece == null) return;
			
			var refNeckPiece :CSnakePiece = m_arrBodyPiece[1];
			if (refNeckPiece == null) return;
			
			if (event.keyCode == Keyboard.LEFT)
			{
				if (refNeckPiece.GetMapPosX() != refHeadPiece.GetMapPosX()-1)
					m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_LEFT;
			}
			else if (event.keyCode == Keyboard.RIGHT)
			{
				if (refNeckPiece.GetMapPosX() != refHeadPiece.GetMapPosX()+1)
					m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_RIGHT;
			}
			else if (event.keyCode == Keyboard.UP)
			{
				if (refNeckPiece.GetMapPosY() != refHeadPiece.GetMapPosY()-1)
					m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_UP;
			}
			else if (event.keyCode == Keyboard.DOWN)
			{
				if (refNeckPiece.GetMapPosY() != refHeadPiece.GetMapPosY()+1)
					m_nTurnDirection = CSnakePiece.MOVE_DIRECTION_DOWN;
			}
		}
		
	}
}